Pacote Shiny

André, Bruno, Caio, Gabriel e João Gabriel

Importando pacotes que serão utilizados para elaboração dos códigos

if(!require("pacman")) install.packages("pacman")
pacman::p_load("shiny",
               "tidyverse",
               "babynames",
               "DT",
               "shinythemes",
               "gapminder",
               "shinydasboard")

Sumário

  1. Introdução
  2. Estrutura Básica e Geral de um App Shiny
  3. Tipos de Inputs e Outputs
  4. Programação Reativa
  5. Tipos de Layout e Temas
  6. Dashboards no Shiny

Introdução

Funcionalidade

Shiny é um pacote do R (também disponível para o Python) que permite a criação e publicação de aplicações web sem necessidade de conhecimento prévio em HTML, CSS e JavaScript;

Ou seja, a proposta é facilitar a criação de apps web somente com conhecimento em R.

Estrutura Básica de um App Shiny

Em essência, um App Shiny é gerado a partir de dois objetos e a chamada de uma função:

  • UI (Interface do Usuário)

  • Server

  • Função shinyApp(ui, server)

library(shiny)

ui <- fluidPage(
  
)

server <- function(input, output) {
  
}

shinyApp(ui = ui, server = server)

UI - Interface do Usuário

UI: Definição

O objeto UI é geralmente inicializado pela função fluidPage(), que gerá uma página HTML vazia (também podem ser usadas outras funções para inicializar o UI);

São definidos os tipos de inputs (valores de entrada) e outputs (valores de saída) que o App Shiny apresentará, assim como também a formatação, layout.

ui <- fluidPage(
  titlePanel(),
  sidebarLayout(
    sidebarPanel(
    ),
    mainPanel(
    )
  )
)

Tipos de Input

Estrutura Comum

Há inúmeros tipos de input disponíveis no Shiny.De todo modo, para que sejam reconhecidos e processados, todos ele precisam de dois parâmetros em comum:

  • Parâmetro “inputId”: Trata-se de uma string simples e única que será armazenada dentro da lista “input” que é parâmetro da função server(input, output). Desse modo, deve-se utilizar id´s diferentes para referenciar diferentes inputs;

  • Parâmetro “label”: Trata-se do rótulo/mensagem/instrução que irá aparecer no App Shiny, orientando o usuário sobre como ele deve interagir com as opções de input dadas.

selectInput(inputId = "input1",   # String identificadora do Input
            label = "Selecione uma das opções abaixo.",
            choices = c("A", "B", "C"))

sliderInput(inputId = "input2", # String identificadora do Input
            label = "Selecione um valor de interesse",
            value = 1925,
            min = 1900,
            max = 2000)

Tipos de Input

selectInput()

Dada uma listagem de opções fornecidas, é solicitado ao usuário selecionar uma ou algumas das opções dadas.

selectInput(inputId = 'input1',
            label = 'Escolha uma das opções',
            choices,
            selected = NULL,
            multiple = FALSE,
            ...)

Tipos de Input

checkboxGroupInput()

Semelhante à função selectInput(), mas com uma aparência levemente diferente, e permite selecionar, por padrão, múltiplas opções.

checkboxGroupInput(inputId,
              label,
              choices,
              selected = NULL,
              ...)

Tipos de Input

actionButton()

Pede ao usuário que aperte um botão interativo para que uma certa ação seja realizada no aplicativo.

actionButton(inputId = 'input1',
             label = 'Aperte o Botão para...',
             icon,
             width,
             ...)

Tipos de Input

sliderInput()

A partir de uma barra de rolagem (horizontal), o usuário é informado para selecionar um número dentro de um intervalo de valores estabelecido

sliderInput(inputId,
            label,
            min,
            max,
            value = "valor_default/padrao",
            ...)

Tipos de Input

textInput()

Trata-se de uma função para inserção de inputs em forma de texto.

textInput(inputId,
          label = "Informe seu nome",
          value = "Nome",
          width = NULL,
          placeholder = NULL)

Tipos de Input

passwordInput()

Semelhante a função tenxtInput(), com a diferença que o input informado aparecerá com tarjas (“*”)

passwordInput(input,
              label,
              value,
              width = NULL,
              placeholder = NULL)

Tipos de Input

Além das funções input citadas acima, há várias outras tais como:

dateInput(inputId = 'data1', label = 'Escolha uma data de interesse', value = NULL,
          min = NULL, max = NULL,
          format = "yyyy-mm-dd", ...)
dateRangeInput(inputId = 'int_data', label = 'Defina um intervalo de datas',
               start = NULL, end = NULL, min = NULL,
               max = NULL, format = "yyyy-mm-dd", ...)
numericInput(inputId = 'numero', label = 'Selecione um valor',
             value, min,
             max, step,
             width = NULL)
fileInput(inputId,label,
          multiple = FALSE, accept = NULL,
          width = NULL,
          ...)

Outputs

Tipos de Output

  • Os tipos de output são definidos no primeiro objeto, a UI (interface de usuário).

  • Assim como os inputs, no output também exigem uma identificação:

    • Parâmetro ‘outputId’.
dataTableOutput(outputId = "dataframe")   # Define um dataframe como output

htmlOutput(outputId = "html") # Define um documento HTML como output

imageOutput(outputId = 'image') # Define uma imagem como output

plotOutput(outputId = 'plot') # Define um gráfico como output

tableOutput(outputId = 'tabela') # Define uma tabela como output

textOutput(outputId = 'texto') # Define um texto como output
...

Tipos de Output

  • Feito isso, para que o(s) output(s) definido(s) seja(m) devidamente executado(s) e processado(s) pelo R, faz-se necessário também o uso das render functions (funções de renderização); por exemplo, “renderText({})”;

  • Estas são definidas no objeto Server (falaremos sobre logo a seguir).

ui3 <- fluidPage( 
  textInput("name", "Enter a name:"),
  textOutput("greeting")
)

server3 <- function(input, output, session){
  output$greeting <- renderText({
    paste("Do you prefer dogs or cats,", input$name, "?")
  })
}
  
shinyApp(ui = ui3, server = server3)

Server

Server

  • É nesse objeto que ocorre efetivamente a interação entre os inputs e outputs definidos no UI. Acessamos cada um dos inputs e outputs definidos usando o operador “$” em conjuto com os parâmetros input e/ou ouput do objeto server, ou seja:
    • input$input_id e output$output_id;

Estrutura Básica do Server

server <- function(input, output, session) {
  
  output$output_id
  
  input$input_id
  

}
  • Mas como exatamente cada elemento do parâmetro output pode interagir com um input?”

Server

Render Functions

  • Para que a interação entre input e output efetivamente ocorra, deve-se utilizar o que chamamos de “render functions”, que possuem variados tipos, e são escolhidas com base nos tipos de output que foram definidos no UI (tabela, gráfico, dataframe…). Ou seja, as render functions são o objeto intermediário entre inputs e outputs.

As render functions (funções de renderização) são os objetos que irão efetivamente explicitar os outputs previamente definidos no UI;

server <- function(input, object) {
  output$output_id <- render**function**({
    
    "blackbox"(input$input_id) ## Dentro da render function, algum input armazenado dentro do parâmetro
                              # input como "input_id" recebe algum tipo de transformação de maneira a gerar
                              # um output
    })

}

Server

Render Functions

Alguns exemplos:

Output Render Function
textOutput renderText({})
plotOutput renderPlot({})
tableOutput renderTable({})
imageOutput renderImage({})

Elas possibilitam também a utilização de outros pacotes interativos possuem suporte para renderizar seus outputs no Shiny. Entre eles DT, Plotly e Leaflet

Exemplos de Render Functions

renderPlot()

server <- function(input, output, session){
  output$hist_waiting <- renderPlot({
    hist(faithful$waiting,
         breaks = input$nb_bins,
         col = 'steelblue')
  })
}

Exemplos de Render Functions

renderPlot() + pacote plotly

server <- function(input, output, session){
  # Função para plotar tendências de um nome
  plot_trends <- function(){
    babynames %>% 
      filter(name == input$name) %>% 
      plotly::plot_ly(, x = ~year, y = ~n, type = "bar")
  }
  output$plot_trendy_names <- plotly::renderPlotly(plot_trends())
}

Exemplos de Render Functions

renderTable() + pacote DT

server <- function(input, output){
  output$babynames_table <- DT::renderDT({
    babynames %>% 
      dplyr::slice_sample(prop = .1) %>% 
      DT::datatable()
  })
}

Tipos de Layout e Temas

Layout

Na estrutura básica os inputs são dispostos um embaixo do outro, visando melhorar a visualização do usuário, existem funções que alteram esse padrão:

  • titlePanel(): que implementa um título pro app:

  • sidebarLayout(): essa cria o layout geral de saída e que comporta opcções de disposição de inputs e outputs.

titlePanel()

Aqui a função tem como parametros apenas o label que está em formato de string

ui <- fluidPage(
  titlePanel("Histograma")
)

sidebarLayout()

No layout gerado por essa função divide-se a tela de saída em duas:

  • sidebarPanel(): aqui estarão as entradas do usário;

  • mainPanel(): nesta área são as saídas geradas no server.

inputPanel(
  titlePanel("Baby Name Explorer"),
  sidebarLayout(
    sidebarPanel(
      textInput('name', 'Enter Name', 'David')),
    mainPanel(  
      plotOutput('trend')))
)

Baby Name Explorer

sidebarPanel()

Neste espaço as opções de inputs vistas anteriormente são posicionadas no lado esquerdo da interface;

São possíveis envolopar mais de uma opção de input.

 inputPanel(
   titlePanel('BMI Calculator'),
   sidebarLayout(
     sidebarPanel(
       textInput('name', 'Enter your name'),
       numericInput('height', 'Enter height (in m)', 1.5, 1, 2, step = 0.1),
       numericInput('weight', 'Enter weight (in Kg)', 60, 45, 120),
       actionButton("show_bmi", "Show BMI")
     ),
     mainPanel()
    )
   )

BMI Calculator

mainPanel()

Nessa função vamos incluir as saídas que foram processadas no server e adicionadas no lado direito da interface;

Aqui caso haja uma saída basta apenas utilizar:

 ui <- fluidPage(
   titlePanel('BMI Calculator'),
   sidebarLayout(
     sidebarPanel(
       textInput('name', 'Enter your name'),
       numericInput('height', 'Enter height (in m)', 1.5, 1, 2, step = 0.1),
       numericInput('weight', 'Enter weight (in Kg)', 60, 45, 120),
       actionButton("show_bmi", "Show BMI")
     ),
     mainPanel(
       textOutput("bmi")
     )
   )
 )

tabsetPanel()

Caso existam mais de uma saída e deseja-se criar abas para exibir os resultados vamos usar essa função.

 ui <- fluidPage(
   titlePanel("UFO Sightings"),
   sidebarPanel(
     selectInput("state", "Choose a U.S. state:", choices = unique(usa_ufo_sightings$state)),
     dateRangeInput("dates", "Choose a date range:",
                    start = "1920-01-01",
                    end = "1950-01-01"
     )
   ),
   mainPanel(
     tabsetPanel(
       tabPanel("Plot", plotOutput("shapes")),
       tabPanel("Table", tableOutput("duration_table"))
     )
   )
 )

Programação Reativa

O que é reatividade?

Resumidamente, reatividade em programação é o conceito que torna possível a interação dinâmcica do usuário com o programa, no qual o que é efetivamente executado e exibido se baseia no acompanhamento das mudanças de determinados valores. Por exemplo, se um determinado input muda, um novo cálculo deve ser executado e um novo output gerado.

Programação Reativa

Como funciona a reatividade em apps Shiny?

A reatividade se baseia em uma paradigma diferente de programção, chamado de declarativo. No Shiny, declaramos dentro da função server() quando e quais códigos devem ser executados a depender de cada nova situação. As relações de dependência entre input e output e os consequentes fluxos de execução nesses casos são definidadas e representadas através de um diagrama de reatividade.

Programação Reativa

Diagrama de Reatividade

exemplo:

# ui
ui <- fluidPage(
  selectInput(
    inputId = "variavel_A",
    label = "Variável A",
    choices = names(mtcars)
  ),
  plotOutput(outputId = "histograma_A"),
  selectInput(
    inputId = "variavel_B",
    label = "Variável B",
    choices = names(mtcars)
  ),
  plotOutput(outputId = "histograma_B")
)

Programação Reativa

Diagrama de Reatividade

exemplo:

# server
server <- function(input, output, session) {
  output$histograma_A <- renderPlot({
    print("Gerando histograma A...")
    hist(mtcars[[input$variavel_A]], main = "Histograma A")
  })
  output$histograma_B <- renderPlot({
    print("Gerando histograma B...")
    hist(mtcars[[input$variavel_B]], main = "Histograma B")
  })
}

# app
shinyApp(ui, server)

Programação Reativa

Diagrama de Reatividade

diagrama de reatividade do exemplo

Programação Reativa

Valores Reativos e Funções Observadoras

Para um app Shiny funcionar corretamente, é necessário que o diagrama de reatividade seja definido adequadamente na função server(). E para isso, alguns princípios devem ser observados. O primeiro deles é o seguinte: o diagrama de reatividade deve começar em um valor reativo e terminar em uma função observadora

Programação Reativa

Valores Reativos e Funções Observadoras

valores reativos são objetos que ativam a reatividade a partir das mudanças em seus valores. O principal exemplo de valor reativo são os próprios objetos do input.

funções observadoras são funções que acompanham as mudanças no valores reativos e executam algo diferente a partir delas. O principal exemplo de função reativas são as funções da famílias render*().

Programação Reativa

Expressões Reativas

Expressões Reativas são objetos especiais muito importantes que atuam como valor reativo e função observadora ao mesmo tempo. São utilizadas em situações onde um valor reativo utilizado dentro de uma função observadora depende outro valor reativo.

Programação Reativa

Expressões Reativas

Sua utilidade se torna mais claro quando levamos em consideração um segundo princípio importante da reatividade no shiny: valores e expressões reativas só podem ser lidas dentro de um contexto reativo.

Ou seja, em um caso onde precisemos criar uma variável que dependa de um input, mas que sirva para ativar a reatividade em outputs diferentes, precisamos de uma expressão reativa, por exemplo.

Programação Reativa

Expressões Reativas

exemplo:

Imagine uma aplicação Shiny que simule o lançar de um dado simples de 6 faces. Queremos que o usário insira o número de lançamentos (tamanho da amostra) desejado e que a partir desse número seja exibido um gráfico de barras com o quantidade de aparições de cada número (distribuição observada) e um frase informando qual valor mais apareceu.

Programação Reativa

Expressões Reativas

exemplo - primeira abordagem:

ui <- fluidPage(
  "INCRÍVEL DADO",
  sliderInput(
    inputId = "tamanho",
    label = "Selecione o número de lançamentos",
    min = 1,
    max = 1000,
    value = 500
  ),
  plotOutput(outputId = "distribuicao"),
  textOutput(outputId = "frase")
)

Programação Reativa

Expressões Reativas

exemplo - primeira abordagem:

server <- function(input, output, session) {
  lancamentos <- sample(1:6, input$tamanho, replace = TRUE)

  output$distribuicao <- renderPlot({
    lancamentos |> 
      table() |> 
      barplot()
  })
  output$frase <- renderText({
    contagem <- table(lancamentos)
    mais_freq <- names(contagem[which.max(contagem)])
    num_ap <- contagem[mais_freq]
    paste("o valor mais sorteado foi o", mais_freq, "com ", num_ap, "aparições")
  })
}
shinyApp(ui, server)
Error in input$tamanho : 
  Can't access reactive value 'tamanho' outside of reactive consumer.
ℹ Do you need to wrap inside reactive() or observe()?

Programação Reativa

Expressões Reativas

Por que deu erro?

Justamente porque não seguimos o segundo princípio, e tentamos acessar o valor reativo input$tamanho dentro da variável comum lancamentos.

Qual é abordagem correta?

Podemos resolver criando o objeto lancamentos como uma expressão reativa através da função observadora reactive().

Programação Reativa

Expressões Reativas

exemplo - abordagem adequada

Dentro de server(), ao invés de:

lancamentos <- sample(1:6, input$tamanho, replace = TRUE)

Fazemos:

lancamentos <- reactive({
    sample(1:6, input$tamanho, replace = TRUE)
  })
  
## Observação: devemos utilizar () ao lado do nome (como uma função)
## para acessar as expressões reativas criadas

Programação Reativa

Funções Para Expressões Reativas

reactive()

A função reactive() cria uma expressão reativa que observa todos os valores reativos presentes dentro de seu código.

No exemplo anterior, criamos a expressão reativa lancamentos cujo valor é recalculado sempre que o valor reativo input$tamanho mudar, e utilizamos seu valor chamando lancamentos() dentro das funções observadoras renderPlot() e renderText().

Programação Reativa

Funções Para Expressões Reativas

eventReactive()

A função eventReactive() é similar, mas observa mudanças em apenas um valor reativo, especificado na chamada da própria função.

Geralmente é utilzada quando queremos atrasar (delay) a ativação da função observadora.

Um caso comum de uso é quando temos um botão na UI, criado com a função actionButton(), por exemplo.

Programação Reativa

Funções Para Expressões Reativas

eventReactive()

Podemos utilizar essa ideia para incrementar o nosso exemplo anterior, de tal forma que os outputs só serão gerados caso o botão seja clicado, independente se os valores de input$tamanho mudarem.

Adicionamos a função actionButton() da seguinte maneira:

actionButton(inputId = "botao", label = "Simular")

Programação Reativa

Funções Para Expressões Reativas

eventReactive()

E atribuímos ao objeto lancamentos dentro do server() a função eventReactive() tendo input$botao como primeiro argumento:

lancamentos <- eventReactive(input$botao, {
    sample(1:6, input$tamanho, replace = TRUE)
  })

Programação Reativa

Outras Funções Observadoras

observe() E observeEvent()

A funções observe() e observetEvent() tem o conceito semelhante a reactive() e eventReactive(), porém possuem uma diferença fundamental: elas não geram expressões reativas, sendo utilizadas apenas para rodar determinado código caso uma mudança em algum valor reativo aconteça. Ou seja, não podemos definir a expressão reativa lancamentos do exemplo anterior com elas.

Programação Reativa

Outras Funções Observadoras

observe() E observeEvent()

Geralmente são utilizadas quando queremos usar a reatividade para disparar (trigger) ações que não estão ligadas à geração de outputs, como o registro de informações em bases de dados ou o envio de e-mails, por exemplo.

Programação Reativa

Outras Funções Importantes

isolate()

Utilizada para isolar um valor reativo específico entre vários, de tal forma que se apenas ele for alterado nenhuma reatividade é disparada.

Programação Reativa

Outras Funções Importantes

reactiveVal() E reactiveValues()

Utilizada para gerar apenas um (reactiveVal) ou mais (reactiveValues) valores reativos mutáveis, diferentes do objetos do input, que são imutáveis.

Pode ser usada em alguns casos quando queremos controlar valores reativos dentro do server(), e renderizar mudanças na UI a partir deles.

Programação Reativa

Outras Funções Importantes

req() E validate()

Utilizada para validar valores reativos. Nesse contexto inválido indica um objeto:

• FALSE
• NULL
• "", uma string vazia
• Um vetor vazio
• Um vetor que contenha apenas NA
• Um vetor lógico que contenha apenas FALSE ou NA
• Um objeto com classe try-error
• Um valor reativo que represente um actionButton() que ainda não foi clicado

Programação Reativa

Outras Funções Importantes

req() E validate()

A função req() retorna um erro silencioso. Com o qual podemos definir que o server() e a ui() devem manter as coisas como estavam antes.

Já a função validate() permite customizar o erro retornado.

Dashboard no Shiny

Shiny Dashboard

  • Uma das formas de se criar dashboards utilizando as funcionalidades do Shiny é utilizando o pacote shinydashboard
library(shinydashboard)

header <- dashboardHeader(title = "My first dashboard")

sidebar <- dashboardSidebar()

body <- dashboardBody()

ui <- dashboardPage(header, sidebar, body)

server <- function(input, output){
  
  shinyApp(ui, server)

Shiny Dashboard

  • Mudando parâmetros no header
  # Definindo novos parâmetros no header
  header <- dashboardHeader(title = "My first dashboard",
                            titleWidth = 300,
                            dropdownMenu(type = "messages"),
                            dropdownMenu(type = "notifications")
                            
  )
  
  ui <- dashboardPage(header, sidebar, body)
  
  server <- function(input, output) {
  }
  
  shinyApp(ui, server)

Shiny Dashboard

  • Mudando parâmetros na sidebar
# Definindo a sidebar e seus parâmetros
  sidebar <- dashboardSidebar(width = 300,
                              sidebarMenu(
                                id = "pages",
                                menuItem("Many charts", tabName = "charts",
                                         icon = icon("chart-line")),
                                menuItem("Statistics", tabName = "stats",
                                         icon = icon("file-excel"))
                              ))
  
  ui <- dashboardPage(header, sidebar, body)
  
  server <- function(input, output) {
  }
  
  shinyApp(ui, server)

Shiny Dashboard

  • É possível adicionar subtabs na sidebar
  # Adicionando subtabs na sidebar
  sidebar <- dashboardSidebar(
    width = 300,
    sidebarMenu(
      id = "pages",
      menuItem("Many charts", tabName = "charts",
               icon = icon("chart-line")),
      menuItem("Statistics", tabName = "stats",
               icon = icon("file-excel"),
               menuSubItem("Team 1", tabName = "team1",
                           icon = icon("user")))
    ))
  
  ui <- dashboardPage(header, sidebar, body)
  
  server <- function(input, output) {
  }
  shinyApp(ui, server)

Shiny Dashboard

  • Assim como adicionar inputs
# Adicionando inputs na sidebar
  
  sidebar <- dashboardSidebar(width = 300,
                              sidebarMenu(
                                id = "pages",
                                menuItem("Many charts", tabName = "charts",
                                         icon = icon("chart-line")),
                                menuItem("A couple of checkboxes",
                                         checkboxGroupInput("checkboxes",
                                                            "Days of the week",
                                                            choices = c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")))
                              ))
  
  ui <- dashboardPage(header, sidebar, body)
  
  server <- function(input, output) {
  }
  
  shinyApp(ui, server)

Shiny Dashboard

  • Por fim é possível desativar a sidebar
  # Desativando a sidebar
  
  sidebar <- dashboardSidebar(disable = TRUE)
  
  ui <- dashboardPage(header, sidebar, body)
  
  server <- function(input, output) {
  }
  
  shinyApp(ui, server)